#pragma once
#include <iostream>
#include <queue>
#include <mutex>
#include <pthread.h>
#include "lockGuard.hpp"
const int gDefaultCap = 7;

template <class T>
class BlockQueue
{
private:
    bool isQueueEmpty()
    {
        return _bq.size() == 0;
    }
    bool isQueueFull()
    {
        return _bq.size() == _capacity;
    }

public:
    BlockQueue(int capacity = gDefaultCap) 
        : _capacity(capacity)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_Empty, nullptr);
        pthread_cond_init(&_Full, nullptr);
    }
    void push(const T& in) // 生产者
    {
        lockGuard lockgrard(&_mtx); // 自动调用构造函数
        //pthread_mutex_lock(&_mtx);

        // pthread_cond_wait: 只要是一个函数，就可能调用失败，可能存在 伪唤醒 的情况，所以用while
        while(isQueueFull()) //1. 先检测当前的临界资源是否能够满足访问条件
        {
            pthread_cond_wait(&_Full, &_mtx); // 满的时候就在_Full这个条件变量下等待
            // 此时思考：我们是在临界区中，我是持有锁的，如果我去等待了，锁该怎么办呢？
            // 所以pthread_cond_wait第二个参数是一个锁，当成功调用wait之后，传入的锁，会被自动释放
            // 当我被唤醒时，我从哪里醒来呢？->从哪里阻塞挂起，就从哪里唤醒, 被唤醒的时候，我们还是在临界区被唤醒的
            // 当我们被唤醒的时候，pthread_cond_wait，会自动帮助我们线程获取锁
        }

        _bq.push(in); // 2. 队列不为空或者被唤醒 -> 访问临界资源，100%确定，资源是就绪的

        pthread_cond_signal(&_Empty); // 唤醒
        // pthread_mutex_unlock(&_mtx); // 解锁
    } // 出了代码块自动调用析构函数

    void pop(T* out)
    {
        lockGuard lockgrard(&_mtx); // 自动调用构造函数
        // pthread_mutex_lock(&_mtx);

        while (isQueueEmpty())
        {
            pthread_cond_wait(&_Empty, &_mtx);
        }

        *out = _bq.front(); // 访问临界资源
        _bq.pop();

        pthread_cond_signal(&_Full); // 唤醒
        // pthread_mutex_unlock(&_mtx); // 解锁
    } // 出了代码块自动调用析构函数
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_Empty);
        pthread_cond_destroy(&_Full);
    }

protected:
    std::queue<T> _bq;     // 阻塞队列
    int _capacity;         // 容量上限
    pthread_mutex_t _mtx;  // 通过互斥锁保证队列安全
    pthread_cond_t _Empty; // 用它来表示bq 是否空的条件
    pthread_cond_t _Full;  //  用它来表示bq 是否满的条件
};

// #pragma once

// #include <iostream>
// #include <queue>
// #include <mutex>
// #include <pthread.h>
// #include "lockGuard.hpp"

// // #define INI_MTX(mtx) pthread_mutex_init(&mtx, nullptr)
// // #define INI_COND(cond) pthread_cond_init()

// const int gDefaultCap = 7;

// template <class T>
// class BlockQueue
// {
// private:
//     bool isQueueEmpty()
//     {
//         return _bq.size() == 0;
//     }
//     bool isQueueFull()
//     {
//         return _bq.size() == _capacity;
//     }

// public:
//     BlockQueue(int capacity = gDefaultCap) : _capacity(capacity)
//     {
//         pthread_mutex_init(&_mtx, nullptr);
//         pthread_cond_init(&_Empty, nullptr);
//         pthread_cond_init(&_Full, nullptr);
//     }
//     void push(const T& in) // 生产者
//     {
//         pthread_mutex_lock(&_mtx);

//         // pthread_cond_wait: 但是只要是一个函数，就可能调用失败
//         // pthread_cond_wait: 可能存在 伪唤醒 的情况

//         while(isQueueFull()) //1. 先检测当前的临界资源是否能够满足访问条件
//         {
//             pthread_cond_wait(&_Full, &_mtx); // 满的时候就在_Full这个条件变量下等待
//             // 此时思考：我们是在临界区中，我是持有锁的，如果我去等待了，锁该怎么办呢？
//             // 所以pthread_cond_wait第二个参数是一个锁，当成功调用wait之后，传入的锁，会被自动释放
//             // 当我被唤醒时，我从哪里醒来呢？->从哪里阻塞挂起，就从哪里唤醒, 被唤醒的时候，我们还是在临界区被唤醒的
//             // 当我们被唤醒的时候，pthread_cond_wait，会自动帮助我们线程获取锁
//         }

//         _bq.push(in); // 2. 队列不为空或者被唤醒 -> 访问临界资源，100%确定，资源是就绪的
//         // if(_bq.size() >= _capacity/2) pthread_cond_signal(&_Empty);
//         pthread_cond_signal(&_Empty);
//         pthread_mutex_unlock(&_mtx);
//         lockGuard lockgrard(&_mtx); // 自动调用构造函数

//     } // 自动调用lockgrard 析构函数

//     void pop(T* out)
//     {
//         lockGuard lockguard(&_mtx);
//         // pthread_mutex_lock(&_mtx);
//         while (isQueueEmpty())
//         {
//             pthread_cond_wait(&_Empty, &_mtx);
//         }

//         *out = _bq.front(); // 访问临界资源
//         _bq.pop();

//         pthread_cond_signal(&_Full); // 唤醒
//         pthread_mutex_unlock(&_mtx); // 解锁
//     }
//     ~BlockQueue()
//     {
//         pthread_mutex_destroy(&_mtx);
//         pthread_cond_destroy(&_Empty);
//         pthread_cond_destroy(&_Full);
//     }

// private:
//     std::queue<T> _bq;     // 阻塞队列
//     int _capacity;         // 容量上限
//     pthread_mutex_t _mtx;  // 通过互斥锁保证队列安全
//     pthread_cond_t _Empty; // 用它来表示bq 是否空的条件
//     pthread_cond_t _Full;  //  用它来表示bq 是否满的条件
// };